﻿#define  _CRT_SECURE_NO_WARNINGS


#include <iostream>
using namespace std;

#include "Func.h"



// inline内联函数，auto，范围for，nullptr，类(定义,访问限定符,作用域,实例化,对象大小,this指针,封装)





// 
// 7.内联函数
// 
// 
// 
//int main()
//{
//	// inline内联函数不建议声明与定义分离
//	f(1);
//
//	// 函数的call指令，跳转到jmp指令的地址
//	// jmp指令，跳转到函数的地址，
//	// 进入函数中开始执行操作
//
//	// 而内联函数不需要这个函数操作，被认为所有的调用地方都是直接展开。没有call这个概念
//	// 所以inline在声明和定义分离时，被分离的定义无法在编译时正常展开，
//	// 也就导致不能在代码执行时候正常执行。
//	// 
//	// 将inline函数直接定义在头文件中，在编译时直接对函数定义进行展开，
//	// 在执行时调用点直接去头文件中找到函数的定义，去执行
//	//
//
//
//	return 0;
//}











// 8. auto关键字(C++11版本 ， Linux gcc4.6)
// 
// 
// 
// 
// 
// 
// 
// 
// auto自动推导变量的类型
// 
// auto 实际价值 简化代码，当类型很长时，可以考虑让编译器自动推导类型
// 
//int main()
//{
//	int a = 0;
//	// int b = a;
//	auto b = a;
//	auto c = &a;
//
//	// 获取变量实际的类型
//	cout << typeid(b).name() << endl;
//	cout << typeid(c).name() << endl;
//
//
//	return 0;
//}
// 
// 
// 
// 
// 
// 
// 
// typedef的缺点：
// 
// 
//typedef char* pstring;
//int main()
//{
//	const pstring p1 = nullptr;// 失败
//	const pstring* p2;// 成功
//
//	// p1编译失败原因：
//	// const pstring p1  ==  char* const p1
//	// 注意：这里const修饰的是p1 ，而const修饰的变量必须进行初始化。
//
//	// p2编译成功原因：
//	// const pstring* p2  ==  char* const* p2
//	// const 修饰的是一级指针 *p2 ，所以不用初始化
//	//
//
//	return 0;
//}
// 
// 
// 对一个指针typedef的时候需要多加注意！！！
// 
// 
// 
// 
// 
// 
// 
// 
// 
// auto的使用细则
// 
//int main()
//{
//	int a = 0;
//	auto b = &a;
//	auto* c = &a;// 相当于限定c变量必须是个指针变量
//	cout << typeid(c).name() << endl;
//	auto& d = a; // 限定d是一个引用
//
//
//
//	// 遍历数组
//	int array[] = { 1,2,3,4,5,6,7,8,9 };
//	//for (int i = 0; i < sizeof(array) / sizeof(int); i++)
//	//{
//	//	cout << array[i] << " ";
//	//}
//	//cout << endl;
//
//
//	// 范围for（语法糖）  遍历数组
//	// C++，抄其他语法的作业
//	// 但是会让编译变慢，底层还是指针的方式实现的
//	// 
//	// 
//	// 自动依次取数组中的数据赋值给e变量/对象，自动判断结束
//	// 
//	// 
//	for (auto e : array)
//	{
//		cout << e << " ";
//	}
//	cout << endl;
//
//	for (auto x : array)
//	{
//		cout << x << " ";
//	}
//	cout << endl;
//	for (int x : array)// 这样写只能适应数据全部为int类型的数组
//	{
//		cout << x << " ";
//	}
//	cout << endl;
//
//
//	// 注意：
//	// 
//	// 
//	// 1.e只是数组中值的拷贝，e的改变不会影响数组中的原始数据
//	// 
//	// 
//	// 
//	// 若想要改变数组中的数据本身
//	for (auto& e : array)// 规定在auto推导出来的类型后，增加&变为引用类型，将具体的数据赋值给他对应的引用变量即可
//	{
//		e *= 2;// e为每个数组中数据的别名
//		cout << e << " ";
//	}
//	cout << endl;
//
//
//
//	return 0;
//}
// 
// 
// 2.当函数内接收的数组为，数组首元素的下标，不能使用范围for
// 
//void TestFor(int array[])
//{
//	for (auto& e : array)
//		cout << e << endl;
//}
// 
// 
// 
// 
// 
// 
// 
// 
// auto的其他小细节：
// 
// 
// 1.auto可以同时声明多个变量
//int main()
//{
//	
//	auto a = 1, b = 2;
//	//auto c = 3, d = 4.0;// 注意还需要和以前的语法自恰，c d 是两个不同的类型，需要分开来定义
//
//	return 0;
//}
// 
// 
// 
// 2.auto不能做形参
// 
// 此处代码编译失败，auto不能作为形参类型，因为编译器无法对a的实际类型进行推导
//void TestAuto(auto a)
//{}
// 
// 
// 
// 
// 
// 3.auto不能声明数组
// 
//void TestAuto()
//{
//	int a[] = { 1,2,3 };
//	auto b[] = { 4，5，6 };
//}
// 
// 
// 












// 指针的初始化
// 
// 
// 
// 
//void _f(int)
//{
//	cout << "_f(int)" << endl;
//}
//void _f(int*)
//{
//	cout << "_f(int*)" << endl;
//}
// 
// 
//int main()
//{
//	// 对比差异
//	/*int* p1 = NULL;
//	int* p2 = nullptr;*/
//
//	// C++函数重载的bug
//	// 下面这两个函数，都会去调用f(int)
//	// 原因C++中对NULL的定义   ----   在C++中NULL == 0
//	_f(0);
//	_f(NULL);
//
//	// 基于C++ 中 NULL == 0的原因
//	// 打补丁 nullptr == C语言中的NULL   ---  不需要头文件，语法原生支持
//	// 
//	// 本质nullptr == (void*)0
//	_f(nullptr);
//
//
//	return 0;
//}
// 






















//类与对象(上)
// 
// 
// 
// 
// 1.面向过程和面向对象初步认识
// 2.类的引入
// 3.类的定义
// 4.类的访问限定符及封装
// 5.类的作用域
// 6.类的实例化
// 7.类的对象大小的计算
// 8.类成员函数的this指针
// 
// 
// 





// 
// 2.类的引入
// 
// C++兼容C语言中的结构体
//typedef struct ListNode
//{
//	int val;
//	struct ListNode* next;
//	// C++中的结构体就当做类来处理了
//}LTN;
// 
// 
// 同时C++将C语言中的结构体升级为了，类
// 
// 区别：
// 1.类里面可以有数据，叫做成员变量
// 2.类里面可以定义函数
// 
// 
// 注意：
// 本质C++中的结构体就是类。
// 而C++中的类，本身的名称就可以作为一个类的类型，
// 所以 不需要强制给类的名称 前增加上struct 关键字了
//
//struct ListNode
//{
//	int val;
//	ListNode* next;
//	void f(int a)
//	{
//		;
//	}
//};
//typedef struct ListNode
//{
//	int val;
//	ListNode* next;
//	// C++中的结构体就当做类来处理了
//	void f(int a)
//	{
//		;
//	}
//}LTN;
// 
// 
// 





// 
// 使用C++实现一个栈的类
// 
// 
// 
// 
//#include <stdlib.h>
//#include <assert.h>
//typedef int STDatatype;
//
// 
// //类是一个整体，不需要区分先后顺序
// 
//typedef struct Stack
//{
//
//	// 在类中叫成员函数
//	// 栈初始化
//	void Init(Stack* ps, int size = 4)
//	{
//		// 仅仅是对已有结构体中内部数据进行修改
//		assert(ps);
//
//		// 开辟数组空间
//		ps->a = (STDatatype*)malloc(sizeof(STDatatype) * size);
//		if (ps->a == NULL)
//		{
//			perror("malloc fail");
//			exit(-1);
//		}
//
//		ps->capacity = size, ps->top = 0;
//	}
//	// 栈的销毁
//	void Destroy(Stack* ps)
//	{
//		assert(ps);
//
//		free(ps->a);
//		ps->a = NULL;
//
//		ps->capacity = ps->top = 0;
//	}
//	// 压栈
//	void Push(Stack* ps, STDatatype x)
//	{
//		assert(ps);
//
//		// 检查栈空间是否充足 --- 扩容
//		if (ps->capacity == ps->top)
//		{
//			STDatatype* tmp = (STDatatype*)realloc(ps->a, sizeof(STDatatype) * (ps->capacity) * 2);
//			if (tmp == NULL)
//			{
//				perror("realloc fail");
//				exit(-1);
//			}
//
//			ps->a = tmp;
//
//			// 更新容量标志
//			ps->capacity *= 2;
//		}
//
//		// 增加数据
//		ps->a[ps->top] = x;
//		ps->top++;
//	}
//	// 出栈
//	void Pop(Stack* ps)
//	{
//		assert(ps);
//
//		// 保证栈中还有数据可以出栈
//		assert(!Empty(ps));
//
//		ps->top--;
//	}
//	// 返回栈顶数据
//	STDatatype Top(Stack* ps)
//	{
//		assert(ps);
//
//		// 保证栈顶有数据可以返回
//		assert(!Empty(ps));
//
//		return ps->a[ps->top - 1];
//	}
//	// 判断栈中是否有数据
//	bool Empty(Stack* ps)
//	{
//		assert(ps);
//
//		return ps->top == 0;
//	}
//	// 栈中数据个数
//	size_t Size(Stack* ps)
//	{
//		assert(ps);
//
//		return ps->top;
//	}
//
//
//
//	// 在类中叫成员变量
//	STDatatype* a;
//	int capacity;
//	int top;
//	// 若top初始化为0，top指向栈顶位置的下一个空位的下标，也是当前栈中存储数据的个数
//	// 若top初始化为-1，top指向当前栈顶的位置，则压入下一个元素的下标位置为top + 1
//}ST;


//
// 如何调用
// 
//int main()
//{
//	Stack st;
//	// 创建一个栈
//	st.Init(&st);// C++函数缺省参数
//	// 插入数据
//	st.Push(&st, 1);
//	st.Push(&st, 2);
//	st.Push(&st, 3);
//	// 销毁栈
//	st.Destroy(&st);
//
//	return 0;
//}











// 类的定义
// 
//  
// 
// 
// C++推荐使用class 定义类
// 
// 
// 
//class className
//{
//	// 类体：由成员函数和成员变量组成
//}; // 一定要注意后面的分号
// 
// class为定义类的关键字，ClassName为类的名字，{}中为类的主体，注意类定义结束时后面分号不能省略。
//
// 类体中内容称为类的成员：
// 类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 类如何实现声明和定义分离：
// 
// 
// 
// 若使用class定义类，需要标识访问限定符
// 
// C++中有三种：
// 
// public 公有
// protected 保护
// private 私有
// 
//【访问限定符说明】
//1. public修饰的成员在类外可以直接被访问
//2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
//3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
//4. 如果后面没有访问限定符，作用域就到 } 即类结束。
//5. class的默认访问权限为private，struct为public(因为struct要兼容C)
// 
// 
// 
// 
// 
// 
// 
// 
//#include "Stack.h"
////
//// 如何调用
//// 
//int main()
//{
//	Stack st;
//	// 创建一个栈
//	st.Init(&st);// C++函数缺省参数
//	// 插入数据
//	st.Push(&st, 1);
//	st.Push(&st, 2);
//	st.Push(&st, 3);
//	// 销毁栈
//	st.Destroy(&st);
//
//	return 0;
//}
// 
// 
// 


// 6.类的实例化
// 用类类型创建对象的过程，称为类的实例化


// 类相当于一个别墅的图纸，只规定细节，并没有实际产生
// 类的实例化，相当于实际建造出一个一个的别墅，可以开始使用
//class Date
//{
//public:
//	void Init(int year, int month, int day)// 成员函数的定义
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
////private:
//	// 成员变量名称前面加_用以区分，成员变量和成员函数中的形参
//	// 没有实际开空间就是声明
//	int _year;
//	int _month;
//	int _day;
//};
//
//
//int main()
//{
//	// 类对象的实例化
//	// 相当于实际建造出一个一个的别墅，可以开始使用
//	Date d1;// 这里实际开空间
//	Date d2;
//
//	// 将成员变量设为public
//	d1.Init(2023, 3, 29);
//	d1._day ++;
//	d2._day = 2;
//
//	// 计算类的大小
//	cout << sizeof(d1) << endl;
//	// 为什么成员变量在对象中，成员函数不在对象中？
//	// 每个对象的成员变量是不一样的，需要独立存储
//	// 但每个对象能做的事情却是相同的，每个对象调用的成员函数是一样的，存储在公共区域（代码段）
//
//	// 结论：一个类的大小，实际就是该类中”成员变量”之和，当然要注意内存对齐
//	// 
//	// 
//	// 
//	// 注意：
//	// 当类没有成员变量时，编译器会开出 1B 大小空间给这个实例化对象，用来标识这个对象。
//
//
//
//
//	return 0;
//}



// 为什么编译器能分辨出，在不同的实例化对象调用的同一个成员函数，能具体对应上各自的对象呢？


//class Date
//{
//public:
//	// 我们写的
//	void Init(int year, int month, int day)// 成员函数的定义
//	{
//		//_year = year;
//		//_month = month;
//		//_day = day;
//
//		// 可以在成员函数内部，显示的使用this指针
//		this->_year = year;
//		this->_month = month;
//		this->_day = day;
//
//		cout << this << endl;
//
//	}
//	// 编译器实际处理的   ----   这个必须编译器自己完成，但是可以在成员函数使用this指针
//	//void Init(Date* this, int year, int month, int day)// 成员函数的定义
//	//{
//	//	this->_year = year;
//	//	this->_month = month;
//	//	this->_day = day;
//	//}
//
//
//	void func()
//	{
//		cout << "func()" << endl;
//	}
//
//
//
//	//private:
//		// 成员变量名称前面加_用以区分，成员变量和成员函数中的形参
//		// 没有实际开空间就是声明
//	int _year;
//	int _month;
//	int _day;
//};
//
//
//int main()
//{
//	Date d1;
//	Date d2;
//
//
//	// 同一个类的不同实例化对象，调用同一个成员函数
//	// 我们写的
//	//d1.Init(2022, 3, 29);
//	//d2.Init(2023, 3, 29);
//	// 编译器实际处理的
//	//d1.Init(&d1, 2022, 3, 29);
//	//d2.Init(&d2, 2023, 3, 29);
//
//	// 注意：这个必须编译器自己完成，但是可以在成员函数中显示使用this指针
//
//	// 1.this指针存储在哪里？
//	// 
//	// this 是成员函数 隐含的形参（是编译器自己加的），存储在栈上
//	// 因为this访问的频繁，vs下面通过ecx寄存器存储
//	// 
//	// 
//	// 2.空指针问题
//	//
//	Date* ptr = nullptr;
//	ptr->func();// 可以正常运行
//	// 这里的ptr没有发生解引用，只是标识func函数的类域，隐式形参this == nullptr
//
//	ptr->_year;// 发生解引用，需要到ptr所指向的类中，找成员变量_year，而ptr == nullptr，空指针问题崩溃。
//
//	// 是否发生解引用，取决于是否要在所指向的地址中找东西
//	
//	ptr->Init(2023, 3, 29);// 崩溃，空指针问题 
//	
//	(*ptr).func();// 正常运行
//	// （*ptr） 的本质 还是传递参数给this，和 规定func函数的类域
//	// 
//	//
//
//
//	return 0;
//}













// 封装
// 
// 
// 
// 
// 
// 对比C语言，和C++的封装，实现栈数据结构
// 
// 数据结构本质跟语言无关，不同语言实现的细节不同
// 
// 
// C语言：
// 1.数据 和 方法是分离的
// 2.数据访问控制是自由的，不受限制，是一个整体
// 
// 
// C++：
// 1.数据 和 方法统一封装到类里面
// 2.控制访问方式。可以被外界访问的public公有，只能内部访问的private私有/protected保护
// 
//

